/*
 *   This file is part of ed_pvr.
 *
 *   ed_pvr is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   ed_pvr is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with ed_pvr.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <fcntl.h>
#include <unistd.h>
#include <memory.h>
#include <getopt.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <ctype.h>

//#include "record.h"
#include "timers.h"
#include "utils.h"


void *_timer_time_thread(void *);
int _timers_file_io(TIMER_CONTROL_STRUCT *, int);

// **************************************************

void timer_print_timer2(TIMER_STRUCT * timer)
{
	if (timer)
	{
		printf("[%" PRId64"],[%d],[%li],[%li],[%s],[%s],[%s],[%s],[%s],[%s]\n", timer->data.channel_num, timer->data.id, timer->data.start_epoch, timer->data.end_epoch, timer->data.channel_name, timer->data.title, timer->data.plot_outline, timer->data.plot, timer->data.genre, timer->data.sub_genre);
//		printf("\tStart: %s, End: %s\n", utils_epoch_to_str(timer->data.start_epoch), utils_epoch_to_str(timer->data.end_epoch));
		printf("\tStart: %s", ctime(&timer->data.start_epoch));
		printf("\tEnd: %s", ctime(&timer->data.end_epoch));
/*
		printf("[%d],[%d],[%" PRId64"],[%" PRId64"],[%" PRId64"],[%s],[%s],[%s],[%s],[%s],[%s],[%s]\n",
				timer->data.status, timer->data.id, timer->data.start_epoch, timer->data.end_epoch, timer->data.channel_num, timer->data.channel_name, timer->data.title, timer->data.plot, timer->data.plot_outline, timer->data.genre, timer->data.sub_genre, timer->data.url);
*/
	}
}
void timer_print_timer(TIMER_STRUCT * timer)
{
	if (timer)
	{
		printf("[TIMERS] Timer:\n");
//		printf("[TIMERS]\tshm.handle: %d\n", timer->shm.handle);
//		printf("[TIMERS]\tshm.path: %s\n", timer->shm.path);
//		printf("[TIMERS]\tshm.memsz: %" PRId64"\n", timer->shm.mem_size);
		printf("[TIMERS]\t\tstatus: %d\n", timer->data.status);
		printf("[TIMERS]\t\tID: %d\n", timer->data.id);
		printf("[TIMERS]\t\tstart epoch: %li\n", timer->data.start_epoch);
		printf("[TIMERS]\t\tend epoch: %li\n", timer->data.end_epoch);
		printf("[TIMERS]\t\tchannel_num: %" PRId64"\n", timer->data.channel_num);
		printf("[TIMERS]\t\tchannel_name: %s\n", timer->data.channel_name);
		printf("[TIMERS]\t\ttitle: %s\n", timer->data.title);
		printf("[TIMERS]\t\tplot: %s\n", timer->data.plot);
		printf("[TIMERS]\t\tplot_outline: %s\n", timer->data.plot_outline);
		printf("[TIMERS]\t\tgenre: %s\n", timer->data.genre);
		printf("[TIMERS]\t\tsub_genre: %s\n", timer->data.sub_genre);
		printf("[TIMERS]\t\turl: %s\n", timer->data.url);
	}
}

void timer_print_list(TIMER_CONTROL_STRUCT *control)
{
	int i;
	for (i= 0; i < TIMER_MAX_TIMERS; i++)
	{
		if (control->timers[i] && control->timers[i]->data.status != TIMER_STATUS_INVALID)
		{
			timer_print_timer(control->timers[i]);
		}
	}
}

int timer_count_timer(TIMER_CONTROL_STRUCT *control)
{
	if (control)
		return control->count;
	return -1;
}


int timer_send_timer_socket(TIMER_STRUCT *timer, int socket)
{
	int rc = -1;

//	if (timer && timer->data && timer->extra_data && socket)
	if (timer && timer->extra_data && socket)
	{
		TIMER_STRUCT_EXTRA *extra = NULL;
		TIMER_STRUCT *tmp_timer = timer;
		uint64_t count = 0;
		while (tmp_timer)
		{
			count++;
			tmp_timer = tmp_timer->next;
		}
//		printf("timer_send_timer_socket count: %" PRId64"\n", count);

		if (count == 0)
		{
			printf("timer_send_timer_socket error, count is zero\n");
			return -1;
		}

		rc = write_bytes_to_socket(socket, (uint8_t *)&count, sizeof(count), 5000, NULL);
		if (rc != sizeof(count))
		{
			printf("timer_send_timer_socket error 2\n");
			return -1;
		}

		while (timer)
		{
			if (timer->next == NULL)
				timer->data.is_last = 1;

			extra = (TIMER_STRUCT_EXTRA *)timer->extra_data;
			rc = write_bytes_to_socket(socket, (uint8_t *)&timer->data, sizeof(TIMER_STRUCT_SEND), 5000, NULL);
			if (rc != sizeof(TIMER_STRUCT_SEND))
			{
				printf("timer_send_timer_socket error 2\n");
				return -1;
			}

			rc = write_bytes_to_socket(socket, (uint8_t *)extra, sizeof(TIMER_STRUCT_EXTRA), 5000, NULL);
			if (rc !=  sizeof(TIMER_STRUCT_EXTRA))
			{
				printf("timer_send_timer_socket error 3\n");
				return -1;
			}
			timer = timer->next;
		}
	}
	return rc;
}

int timer_add_timer(TIMER_CONTROL_STRUCT *control, TIMER_STRUCT * timer)
{
	if (control && timer)
	{
		int32_t i;
		int64_t now = (int64_t)time(NULL);

		if (!timer->data.channel_num || !timer->data.start_epoch || !timer->data.end_epoch)
		{
			printf("[TIMER] Add Timer, something not good, num: %" PRId64", start: %li, end: %li\n", timer->data.channel_num, timer->data.start_epoch, timer->data.end_epoch);
			return -1;
		}

		if (timer->data.start_epoch >= timer->data.end_epoch)
		{
			printf("[TIMER] Add Timer, start time is greater than end.  Not adding\n");
			return -2;
		}

		if (timer->data.end_epoch <= now)
		{
			printf("[TIMER] Add Timer, end time is less than now.  Not adding\n");
			return -3;
		}

//		timer->data.start_epoch -= control->pre_pad;
//		timer->data.end_epoch += control->post_pad;
		for (i= 0; i < TIMER_MAX_TIMERS; i++)
		{
			if (!control->timers[i])
				continue;

			if (timer->data.channel_num == control->timers[i]->data.channel_num &&
			   timer->data.start_epoch == control->timers[i]->data.start_epoch  &&
			   timer->data.end_epoch == control->timers[i]->data.end_epoch)
			{
				//printf("Dup\n");
//				pthread_mutex_unlock(&control->timer_mutex);
				return 1;
			}
		}

		for (i= 0; i < TIMER_MAX_TIMERS; i++)
		{
			if (!control->timers[i])
			{
//				control->timers[i] = record_shm_init(timer->data.channel_num, timer->data.start_epoch, timer->data.end_epoch, 1);
				control->timers[i] = calloc(1, sizeof(TIMER_STRUCT));
				if (control->timers[i])
				{

//					printf("BEFORE ****\n");
//					timer_print_timer(control->timers[i]);
//					record_copy_timer(&control->timers[i], timer);
					memcpy(control->timers[i], timer, sizeof(TIMER_STRUCT));
//					printf("AFTER ****\n");
//					timer_print_timer(control->timers[i]);
					control->timers[i]->data.status = TIMER_STATUS_WAITING;
					control->timers[i]->data.id = i+1;
					control->timers[i]->parent = control;
					control->count++;
				}
				break;
			}
		}
//		pthread_mutex_unlock(&control->timer_mutex);
		timer_print_list(control);
		if (i >= TIMER_MAX_TIMERS)
			return 2;
		control->sequence_num++;
		return 0;
	}
	return 1;
}

int timer_stop_timer(TIMER_CONTROL_STRUCT *control, int32_t id)
{
	if (control && id > 0)
	{
		int32_t i;
		for (i= 0; i < TIMER_MAX_TIMERS; i++)
		{
			if (control->timers[i] && control->timers[i]->data.id == id)
			{
/*
				if (control->timers[i]->data.status == TIMER_STATUS_BUSY)
				{
					control->timers[i]->data.end_epoch = (int64_t)time(NULL) - control->post_pad - 1;
					while(control->timers[i]->data.status != TIMER_STATUS_INVALID)
					{
						printf("Waiting for timer to die\n");
						usleep(1000*100);
					}
					// stop and cleanup
					return 0;
				}
				else if (control->timers[i]->data.status == TIMER_STATUS_WAITING)
				{
					memset(&control->timers[i], 0, sizeof(TIMER_STRUCT));
					control->count--;
					control->timers[i]->data.status = TIMER_STATUS_INVALID;
				}
*/
				//printf("Dup\n");
			}
		}
	}
	return 1;
}

int timer_del_timer(TIMER_CONTROL_STRUCT *control, int32_t id)
{
	if (control && id > 0)
	{
		int32_t i;
		for (i= 0; i < TIMER_MAX_TIMERS; i++)
		{
			if (control->timers[i] && control->timers[i]->data.status != TIMER_STATUS_INVALID && control->timers[i]->data.id == id)
			{
				TIMER_STRUCT *tmp = control->timers[i];
				control->timers[i] = NULL;
				tmp->data.status = TIMER_STATUS_DONE;
/*
				while (tmp->pid)
				{
					printf("[TIMER] Waiting for record process to die: %d\n", tmp->pid);
					usleep(1000*500);

				}
*/
				printf("[TIMER] Delete timer: %d\n", tmp->data.id);
//				record_shm_close(tmp);
				control->count--;
				control->sequence_num++;
				return 0;
			}
		}
	}
	else
	{
		if (!control)
			printf("controlis bad\n");
		if (!id)
			printf("id bad\n");
	}
	return 1;
}

TIMER_STRUCT * timer_get_timer_by_id(TIMER_CONTROL_STRUCT *control, uint32_t id)
{
	TIMER_STRUCT *ret = NULL;
	if (control)
	{
		uint32_t i;
//		printf("\ttimer_by_id 1\n");
//		pthread_mutex_lock(&control->timer_mutex);
//		printf("\ttimer_by_id 2\n");

		for (i= 0; i < TIMER_MAX_TIMERS; i++)
		{
			if (control->timers[i] && control->timers[i]->data.status != TIMER_STATUS_INVALID && control->timers[i]->data.id == id)
			{
				ret = control->timers[i];
				break;
			}
		}
//		pthread_mutex_unlock(&control->timer_mutex);

	}

	return ret;
}

TIMER_STRUCT * timer_list_timer(TIMER_CONTROL_STRUCT *control)
{
	TIMER_STRUCT * current = NULL, *first = NULL;
	if (control)
	{
		int32_t i;
//		printf("\ttimer_list_timer 1\n");
//		pthread_mutex_lock(&control->timer_mutex);
//		printf("\ttimer_list_timer 2\n");
		for (i= 0; i < TIMER_MAX_TIMERS; i++)
		{
			if (control->timers[i])
			{
				control->timers[i]->next = NULL;
				control->timers[i]->data.is_last = 0;
			}
		}

		for (i= 0; i < TIMER_MAX_TIMERS; i++)
		{
			if (control->timers[i] && control->timers[i]->data.status != TIMER_STATUS_INVALID)
			{
				if (!first)
				{
					first = current = control->timers[i];
				}
				else
				{
					current->next = control->timers[i];
					current = control->timers[i];
				}
			}
		}
//		pthread_mutex_unlock(control->timer_mutex);

	}
	return first;
}

void *_timer_time_thread(void *p)
{
//	int status = 0;
	int i, count = 0;
	int64_t now = 0;
	TIMER_CONTROL_STRUCT * timer_control = (TIMER_CONTROL_STRUCT *)p;
	printf("[TIMERS] ------ Starting timer thread\n");

	while (timer_control && !timer_control->die)
	{
		now = (int64_t)time(NULL);

//		printf("\ttimer_time_thread\n");
		for(i=0;i < TIMER_MAX_TIMERS; i++)
		{
			if (!timer_control->timers[i])
				continue;

			switch(timer_control->timers[i]->data.status)
			{
				case TIMER_STATUS_WAITING:
					if (now >= timer_control->timers[i]->data.start_epoch && now < timer_control->timers[i]->data.end_epoch)
						timer_control->timers[i]->data.status = TIMER_STATUS_BUSY;
				break;

				case TIMER_STATUS_BUSY:
					if (now >= timer_control->timers[i]->data.end_epoch)
						timer_control->timers[i]->data.status = TIMER_STATUS_DONE;
				break;

				case TIMER_STATUS_DONE:
					timer_del_timer(timer_control, timer_control->timers[i]->data.id);
				break;
			}
		}

		usleep(1000*1000);
		count++;
		if (count > 4)
		{

			timers_save_file(timer_control);
			count = 0;
		}
	}

//	printf("[TIMERS] ------ Exiting timer thread\n");
	timer_control->timer_thread = 0;
	return NULL;
}

TIMER_CONTROL_STRUCT * timers_init(TIMER_ARG_STRUCT *timeropts)
{
	TIMER_CONTROL_STRUCT * ret = calloc(1, sizeof(TIMER_CONTROL_STRUCT));
	ret->state_filename = strdup(timeropts->state_filename);
	ret->extra_data = timeropts->extra_data;
	ret->post_pad = timeropts->post_pad;
	ret->pre_pad = timeropts->pre_pad;
	ret->sequence_num = 1;

	if (file_exists(ret->state_filename) == 0)
	{
		timers_load_file(ret);
		//timer_print_list(ret);
	}
	else
	{
		//printf("[TIMERS] State file does not exist: %s\n", ret->state_filename);
	}
/*
	ret->record_exe_path = strdup(timeropts->record_exe_path);
	ret->record_exe_name = strdup(timeropts->record_exe_name);
	ret->record_dir = strdup(timeropts->record_dir);
	printf("[TIMERS] Setting record exe: %s/%s\n", ret->record_exe_path, ret->record_exe_name);
*/
//	printf("[TIMERS] Setting Pre/Post Padding time: %d / %d\n", ret->pre_pad, ret->post_pad);

	pthread_mutex_init(&ret->timer_mutex, NULL);
	pthread_mutex_init(&ret->file_mutex, NULL);

	pthread_create(&ret->timer_thread, NULL, _timer_time_thread, (void*)ret);

	return ret;
}

int _timers_file_io(TIMER_CONTROL_STRUCT *timer, int mode)
{
	int ret = 0;

	if (timer && timer->state_filename)
	{
		if (mode == O_RDONLY)
			timer->state_fileh = open(timer->state_filename, mode);
		else
			timer->state_fileh = open(timer->state_filename, mode, OUTPUT_FILE_MODE);

		if (timer->state_fileh > 2)
		{
			int tmp = 0;

			if (mode == O_RDONLY)
			{
				tmp = read(timer->state_fileh, &timer->count, sizeof(timer->count));
//				printf("Reading timer count: %d\n", timer->count);
			}
			else
			{
//				printf("Writing timer count: %d\n", timer->count);
				tmp = write(timer->state_fileh, &timer->count, sizeof(timer->count));
			}

			if (tmp == sizeof(timer->count))
			{
				int i = 0;

				if (mode == O_RDONLY)
				{
					TIMER_STRUCT tmp_timer;
					uint32_t t_count = timer->count;
					timer->count = 0;
//					printf("R1: %d\n", t_count);
					for(i = 0; i < t_count; i++)
					{
						memset(&tmp_timer, 0, sizeof(tmp_timer));
						tmp = read(timer->state_fileh, &tmp_timer, sizeof(TIMER_STRUCT));

						if (tmp == sizeof(TIMER_STRUCT))
						{
//						printf("R2\n");
							timer_add_timer(timer, &tmp_timer);
/*
							if (tmp_timer.channel_num && tmp_timer.start_epoch && tmp_timer.end_epoch)
							{
								printf("Doing SHM on statefile: %" PRId64", %" PRId64", %" PRId64"\n", tmp_timer.channel_num, tmp_timer.start_epoch, tmp_timer.end_epoch);
								timer->timers[i] = record_shm_init(tmp_timer.channel_num, tmp_timer.start_epoch, tmp_timer.end_epoch);
//								printf("BEFORE ****\n");
//								timer_print_timer(timer->timers[i]);
								record_copy_timer(timer->timers[i], &tmp_timer);
								printf("AFTER ****\n");
								timer_print_timer(timer->timers[i]);
							}
*/
						}
					}
				}
				else
				{
					int c = 0;

					for (i= 0; i < TIMER_MAX_TIMERS; i++)
					{
						if (timer->timers[i] && timer->timers[i]->data.status > 0)
						{
							tmp = write(timer->state_fileh, timer->timers[i], sizeof(TIMER_STRUCT));
							c++;
						}
						if (c == timer->count)
							break;
					}
				}
			}
			else
			{
				printf("[TIMER] Reading/writing is not good\n");
			}
			close(timer->state_fileh);
		}
	}
	return ret;
}

int timers_load_file(TIMER_CONTROL_STRUCT *timer)
{
	int i;
	_timers_file_io(timer, O_RDONLY);
	for (i=0; i < TIMER_MAX_TIMERS; i++)
	{
		if (timer->timers[i])
		{
			timer->timers[i]->parent = timer;
			timer->timers[i]->extra_data = NULL;
		}
	}
	return 0;
}

int timers_save_file(TIMER_CONTROL_STRUCT *timer)
{
	pthread_mutex_lock(&timer->file_mutex);
	_timers_file_io(timer, O_WRONLY | O_CREAT | O_TRUNC);
	pthread_mutex_unlock(&timer->file_mutex);
	return 0;
}

#ifdef _MAIN_

TIMER_ARG_STRUCT timeropts = 	{	.state_filename = "timers.bin",
								};

TIMER_CONTROL_STRUCT * tval = NULL;


void display_usage(char *exe)
{
	fprintf(stderr,"bleah\n");
}

static const char *optString = "a:l:p:m:c:d:?h";

static const struct option longOpts[] =
{
	{ "listenip", required_argument, NULL, 'l' },
	{ "listenport", required_argument, NULL, 'p' },
	{ "tuners", required_argument, NULL, 'm' },
	{ "hdhrip", required_argument, NULL, 'c' },
	{ "hdhrport", required_argument, NULL, 'd' },

	{ "help", no_argument, NULL, 'h' },
	{ NULL, no_argument, NULL, 0 }
};

uint8_t get_opts(int argc, char *const argv[])
{
	int index = 0;
	int opt = getopt_long( argc, argv, optString, longOpts, &index );

	if (opt == -1 )
	{
		display_usage(argv[0]);
		return 1;
	}


	while ( opt != -1 )
	{
		switch ( opt )
		{
			case 'l':
//				__bindip = optarg;
				break;

			case 'p':
//				__bindport = atoi(optarg);
				break;

			case 'm':
//				__maxtuners = atoi(optarg);
				break;

			case 'c':
//				__hdhr_channels_file = optarg;
				break;

			case 'a':
//				__hdhr_dev_name = optarg;
				break;

			case 'd':
//				__devport = atoi(optarg);
				break;

			case 'h':
			case '?':
				display_usage(argv[0]);
				break;

			default:
				break;
		}
		opt = getopt_long( argc, argv, optString, longOpts, &index );
	}
	return 0;
}

void sigint_handler()
{
	tval->die = 1;
}

void sigpipe_handler()
{
//   printf("SIGPIPE caught\n");
}

int main(int argc, char *argv[])
{
	signal(SIGPIPE,sigpipe_handler);
	signal(SIGINT,sigint_handler);

	tval = timers_init(&timeropts);
	usleep(1000 *1000 * 10);
//	while(tval && !tval->die && tval->timer_thread)
//		usleep(1000);

	while(tval->timer_thread)
		usleep(1000);

	printf("BYE!\n");
	return 0;
}
#endif

